package com.sq.gateway.filter;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONUtil;
import com.sq.api.dto.SysOperLogDto;
import com.sq.common.constants.Constants;
import com.sq.common.web.domain.AjaxResult;
import org.bouncycastle.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author Li sir
 * @Date 2024/10/8
 */

@Component
public class AccessLogFilter implements GlobalFilter, Ordered {

    @Autowired
    private ModifyResponseBodyGatewayFilterFactory modifyResponseBodyFilter;

    private static final String User_Agent = "User-Agent";

    List<SysOperLogDto> sysOperLogDtoList = new ArrayList<SysOperLogDto>();
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Date startTime = new Date();
        // 将 Request 中可以直接获取到的参数，设置到网关日志
        ServerHttpRequest request = exchange.getRequest();
        String methodName = request.getMethod().name();
        SysOperLogDto sysOperLogDto = new SysOperLogDto();
        sysOperLogDto.setOperId(UUID.randomUUID().toString());
        sysOperLogDto.setRequestMethod(methodName);
        sysOperLogDto.setOperatorType(1);
        sysOperLogDto.setOperUrl(request.getURI().toString());
        List<String> list = request.getHeaders().get(User_Agent);
        if (!Objects.isNull(list) && !list.isEmpty()){
            String str = list.get(0);
            sysOperLogDto.setOperLocation(str);
            UserAgent ua = UserAgentUtil.parse(str);
            sysOperLogDto.setBusinessType(ua.getBrowser().toString() + "-" + ua.getVersion());
        }
        sysOperLogDto.setOperIp("");
        sysOperLogDto.setOperTime(startTime);
        if (HttpMethod.GET.name().equals(methodName)){
            sysOperLogDto.setRequestMethod(methodName);
        }else if (HttpMethod.POST.name().equals(methodName)){
            sysOperLogDto.setRequestMethod(methodName);
        }

        return modifyResponseBodyFilter.apply(
                new ModifyResponseBodyGatewayFilterFactory.Config().setRewriteFunction(Object.class, Object.class, (swe, responseBody)->{
                    Date endTime = new Date();
                    long diff = DateUtil.between(endTime, startTime, DateUnit.MS, true);
                    sysOperLogDto.setCostTime(diff);
                    sysOperLogDto.setJsonResult(JSONUtil.toJsonStr(responseBody));

                    Thread thread = new Thread(() -> {
                        if (sysOperLogDtoList.size() <= 10){
                            sysOperLogDtoList.add(sysOperLogDto);
                        }else {
                            // todo 如果es掉线，插入数据库，定时任务同步到es中
//                            elasticSearchDocTemplete.batchCreateDoc(sysOperLogDtoList, Constants.LOG_INDEX_NAME);
                            sysOperLogDtoList.clear();
                        }
                    }, "gateway-es-insertlog");
                    thread.start();
                    if (Objects.isNull(responseBody) ){
                        AjaxResult success = AjaxResult.success();
                        return Mono.just(success);
                    }
                    return Mono.just(responseBody);

                })).filter(exchange, chain);
    }

    private static String toRaw(Flux<DataBuffer> body) {
        AtomicReference<String> rawRef = new AtomicReference<>();
        body.subscribe(buffer -> {
            byte[] bytes = new byte[buffer.readableByteCount()];
            buffer.read(bytes);
            DataBufferUtils.release(buffer);
            rawRef.set(Strings.fromUTF8ByteArray(bytes));
        });
        return rawRef.get();
    }


    @Override
    public int getOrder() {
        return -100;
    }
}
